/* FINCoS Framework
 * Copyright (C) 2013 CISUC, University of Coimbra
 *
 * Licensed under the terms of The GNU General Public License, Version 2.
 * A copy of the License has been included with this distribution in the
 * fincos-license.txt file.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version. This program is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 */


package pt.uc.dei.fincos.controller.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map.Entry;

import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import pt.uc.dei.fincos.basic.EventType;
import pt.uc.dei.fincos.driver.Scheduler.ArrivalProcess;
import pt.uc.dei.fincos.driver.SyntheticWorkloadPhase;

/**
 * Form that allows editing the parameters of a new or existing synthetic
 * workload phase.
 *
 * @author  Marcelo R.N. Mendes
 *
 */
@SuppressWarnings({"serial", "rawtypes" })
public class SyntheticPhasePanel extends javax.swing.JPanel {

    /** serial id. */
    private static final long serialVersionUID = 1L;


    private final PhaseDetail owner;

    public SyntheticPhasePanel(PhaseDetail owner) {
        this.owner = owner;
        initComponents();
        addListeners();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        schemaTablePopup = new javax.swing.JPopupMenu();
        dataGenRadioGroup = new javax.swing.ButtonGroup();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        dataGenRTRadio = new javax.swing.JRadioButton();
        dataGenDSRadio = new javax.swing.JRadioButton();
        dataGenSeedCheck = new javax.swing.JCheckBox();
        dataGenSeedField = new javax.swing.JTextField();
        durationLbl = new javax.swing.JLabel();
        durationTextField = new javax.swing.JTextField();
        finalRateTextField = new javax.swing.JTextField();
        poissonCheckBox = new javax.swing.JCheckBox();
        schemaLbl = new javax.swing.JLabel();
        deterministicMixCheckBox = new javax.swing.JCheckBox();
        jLabel4 = new javax.swing.JLabel();
        initialRateLbl = new javax.swing.JLabel();
        finalRateLbl = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        initialRateTextField = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        schemaTable = new javax.swing.JTable();

        setBorder(javax.swing.BorderFactory.createTitledBorder(""));
        setMaximumSize(new java.awt.Dimension(460, 397));
        setMinimumSize(new java.awt.Dimension(1, 1));
        setPreferredSize(new java.awt.Dimension(460, 397));

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Data Generation Options"));

        jLabel1.setText("Generate Events' Data:");

        dataGenRadioGroup.add(dataGenRTRadio);
        dataGenRTRadio.setText("In runtime");
        dataGenRTRadio.setToolTipText("Generate events during load submission.");

        dataGenRadioGroup.add(dataGenDSRadio);
        dataGenDSRadio.setText("Before test (save to datafile).");
        dataGenDSRadio.setToolTipText("Generate events to a data file before test starts. Events will be read from it during test.");

        dataGenSeedCheck.setText("Use a fixed seed:");
        dataGenSeedCheck.setToolTipText("Random number generation");

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel1)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(dataGenRTRadio)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(dataGenDSRadio)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(dataGenSeedCheck)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(21, 21, 21)
                        .addComponent(dataGenSeedField, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addGap(20, 20, 20))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(11, 11, 11)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(dataGenSeedCheck))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(dataGenRTRadio)
                    .addComponent(dataGenDSRadio)
                    .addComponent(dataGenSeedField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        durationLbl.setText("Duration");

        durationTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        durationTextField.setToolTipText("Phase duration in seconds");

        finalRateTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        finalRateTextField.setToolTipText("Initial event submission rate, in events per second");

        poissonCheckBox.setText("Poisson Process");
        poissonCheckBox.setToolTipText("Events' interarrival times are exponentially distributed.");

        schemaLbl.setText("Schema");

        deterministicMixCheckBox.setText("Deterministic Mix (all types have the same frequency)");
        deterministicMixCheckBox.setToolTipText("Event types are generated in a predictable and repeatable order.");

        jLabel4.setText("sec");

        initialRateLbl.setText("Initial Rate");

        finalRateLbl.setText("Final Rate");

        jLabel7.setText("events/sec");

        initialRateTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        initialRateTextField.setToolTipText("Initial event submission rate, in events per second");

        schemaTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null}
            },
            new String [] {
                "Type", "Columns", "Mix"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                true, false, true
            };

            @Override
            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        jScrollPane1.setViewportView(schemaTable);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jScrollPane1, 0, 0, Short.MAX_VALUE))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addGap(10, 10, 10)
                        .addComponent(deterministicMixCheckBox))
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                    .addComponent(durationTextField)
                                    .addComponent(durationLbl))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(jLabel4)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(initialRateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(initialRateLbl))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(finalRateLbl)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(finalRateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(jLabel7)
                                        .addGap(24, 24, 24)
                                        .addComponent(poissonCheckBox))))))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addGap(10, 10, 10)
                        .addComponent(schemaLbl)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(initialRateLbl)
                            .addComponent(finalRateLbl))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(durationLbl)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
                    .addComponent(durationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(initialRateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(finalRateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel7)
                    .addComponent(jLabel4))
                .addGap(18, 18, 18)
                .addComponent(schemaLbl)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 126, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(deterministicMixCheckBox)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 46, Short.MAX_VALUE)
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
            .addGroup(javax.swing.GroupLayout.Alignment.CENTER, layout.createSequentialGroup()
                .addGap(30, 30, 30)
                .addComponent(poissonCheckBox)
                .addGap(339, 339, 339))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void addListeners() {
        schemaTablePopup = new javax.swing.JPopupMenu();

        schemaTable.getColumnModel().getColumn(0).setPreferredWidth(50);
        schemaTable.getColumnModel().getColumn(1).setPreferredWidth(200);
        schemaTable.getColumnModel().getColumn(2).setPreferredWidth(30);

        schemaTable.addMouseListener(new PopupListener(schemaTablePopup));
        JMenuItem addPhaseMenuItem = new JMenuItem("Add...");
        JMenuItem deletePhaseMenuItem = new JMenuItem("Delete");

        addPhaseMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                owner.openTypeDetail(null);
            }
        });

        schemaTable.getModel().addTableModelListener(new TableModelListener() {
            public void tableChanged(TableModelEvent e) {
               int row = e.getFirstRow();
               int col = e.getColumn();
               if (row < schemaTable.getRowCount() - 1 && col != -1
                   && schemaTable.isEditing()) {
                   String newAttName = (String) schemaTable.getValueAt(row, col);
                   EventType oldType =  owner.syntheticTypes.get(row);
                   EventType newType = new EventType(newAttName, oldType.getAttributes());
                   owner.syntheticTypes.set(row, newType);
               }
            }
          });

        deletePhaseMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int index = schemaTable.getSelectedRow();

                if (index > -1 && index < owner.syntheticTypes.size()) {
                    owner.syntheticTypes.remove(index);
                    ((DefaultTableModel) schemaTable.getModel()).removeRow(index);
                } else {
                    JOptionPane.showMessageDialog(null, "Select a type to delete");
                }
            }
        });

        schemaTablePopup.add(addPhaseMenuItem);
        schemaTablePopup.add(deletePhaseMenuItem);

        schemaTable.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    JTable source = (JTable) e.getSource();
                    if (source.isEnabled()) {
                        int selected = source.getSelectedRow();
                        if (selected > -1 && selected < owner.syntheticTypes.size()) {
                            owner.openTypeDetail(owner.syntheticTypes.get(selected));
                        }
                    }
                }
            }
        });

        dataGenSeedCheck.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                dataGenSeedField.setEnabled(dataGenSeedCheck.isSelected());
            }
        });

        dataGenRTRadio.setSelected(true);
    }

    /**
     * Fills the UI with the parameters of the workload phase.
     *
     * @param phase workload parameters
     */
    public void fillProperties(SyntheticWorkloadPhase phase) {
        durationTextField.setText(phase.getDuration() + "");
        initialRateTextField.setText(phase.getInitialRate() + "");
        finalRateTextField.setText(phase.getFinalRate() + "");

        poissonCheckBox.setSelected(phase.getArrivalProcess() == ArrivalProcess.POISSON);

        DefaultTableModel model = (DefaultTableModel) schemaTable.getModel();
        int rowCount = model.getRowCount();
        for (int i = 0; i < rowCount; i++) {
            model.removeRow(0);
        }
        if (phase.getSchema() != null) {
            for (Entry<EventType, Double> e : phase.getSchema().entrySet()) {
                owner.syntheticTypes.add(e.getKey());
                ((DefaultTableModel) schemaTable.getModel()).addRow(
                        new Object[] {e.getKey().getName(),
                        e.getKey().getAttributesNamesList(),
                        "" + e.getValue()});
            }
            ((DefaultTableModel) schemaTable.getModel()).addRow(
                    new Object[] {null, null, null});
        }

        deterministicMixCheckBox.setSelected(phase.isDeterministicEventMix());

        if (phase.getDataGenMode() == SyntheticWorkloadPhase.RUNTIME) {
            dataGenRTRadio.setSelected(true);
        } else if (phase.getDataGenMode() == SyntheticWorkloadPhase.DATASET) {
            dataGenDSRadio.setSelected(true);
        }
        if (phase.getRandomSeed() != null) {
            dataGenSeedCheck.setSelected(true);
            dataGenSeedField.setText("" + phase.getRandomSeed());
        }

        this.validate();
        this.repaint();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    protected javax.swing.JRadioButton dataGenDSRadio;
    protected javax.swing.JRadioButton dataGenRTRadio;
    private javax.swing.ButtonGroup dataGenRadioGroup;
    protected javax.swing.JCheckBox dataGenSeedCheck;
    protected javax.swing.JTextField dataGenSeedField;
    protected javax.swing.JCheckBox deterministicMixCheckBox;
    private javax.swing.JLabel durationLbl;
    protected javax.swing.JTextField durationTextField;
    private javax.swing.JLabel finalRateLbl;
    protected javax.swing.JTextField finalRateTextField;
    private javax.swing.JLabel initialRateLbl;
    protected javax.swing.JTextField initialRateTextField;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    protected javax.swing.JCheckBox poissonCheckBox;
    private javax.swing.JLabel schemaLbl;
    protected javax.swing.JTable schemaTable;
    private javax.swing.JPopupMenu schemaTablePopup;
    // End of variables declaration//GEN-END:variables

}
